package net.xo68.destiny.manager.config;

import lombok.extern.slf4j.Slf4j;
import net.xo68.destiny.contract.common.DistributedLockKeys;
import net.xo68.destiny.contract.common.SystemConstants;
import net.xo68.destiny.contract.domain.node.NodeRegistry;
import net.xo68.destiny.contract.enums.NodeTypeEnum;
import net.xo68.destiny.core.discovery.NodeDiscoveryRunner;
import net.xo68.destiny.core.discovery.NodeMapStore;
import net.xo68.destiny.core.lock.DistributedLock;
import net.xo68.destiny.core.lock.DistributedLockManager;
import net.xo68.destiny.core.lock.DistributedLockStatus;
import net.xo68.destiny.core.util.IpUtil;
import net.xo68.destiny.core.util.OSUtils;
import net.xo68.destiny.core.util.TimeUtils;
import net.xo68.destiny.manager.converter.NodeHeartbeat2NodeRegistryConverter;
import net.xo68.destiny.manager.entity.NodeHeartbeat;
import net.xo68.destiny.manager.entity.NodeMonitor;
import net.xo68.destiny.manager.service.NodeHeartbeatService;
import net.xo68.destiny.manager.service.NodeMonitorService;
import net.xo68.destiny.starter.common.SchedulerCommonProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;


/**
 * 管理端注册发现定时处理实现
 * @author wuxie
 * @version 2020-11-19
 */
@Slf4j
@Component
public class NodeDiscoveryRunnerImpl implements NodeDiscoveryRunner {

    private static final String SERVER_PORT="server.port";

    @Autowired
    private Environment environment;
    @Autowired
    private SchedulerCommonProperties schedulerCommonProperties;
    @Autowired
    private NodeHeartbeatService nodeHeartbeatService;
    @Autowired
    private NodeHeartbeat2NodeRegistryConverter nodeHeartbeat2NodeRegistryConverter;

    @Autowired
    private NodeMonitorService nodeMonitorService;

    @Autowired
    private DistributedLockManager distributedLockManager;

    @Override
    public void startLoad(NodeMapStore nodeMapStore) {
        //log.info("管理端注册发现 启动加载...");
        final List<NodeHeartbeat> list = nodeHeartbeatService.validList(schedulerCommonProperties.getHeartbeatPeriodSecond() * 3L);
        final List<NodeRegistry> nodeRegistryList=convert(list);
        nodeMapStore.merge(nodeRegistryList);

        //registrySelf(nodeMapStore);

    }

    @Override
    public void execute(NodeMapStore nodeMapStore) {
        //log.info("管理端注册发现 定时处理...");
        //认为3个周期内的节点都是正常的
        try{
            final List<NodeHeartbeat> list = nodeHeartbeatService.validList(schedulerCommonProperties.getHeartbeatPeriodSecond() * 3L);
            final List<NodeRegistry> nodeRegistryList=convert(list);
            nodeMapStore.merge(nodeRegistryList);

            registrySelf(nodeMapStore);

            String lockHold=IpUtil.getIpPort(schedulerCommonProperties.getNodeRealIp(),environment.getProperty(SERVER_PORT));
            DistributedLock lock = distributedLockManager.tryLock(DistributedLockKeys.REMOVE_EXPIRED_HEARTBEAT, lockHold, 3L, schedulerCommonProperties.getHeartbeatPeriodSecond());
            if(lock!=null){
                //log.debug(" > > 移除过期心跳信息");
                //下面的应该是加锁一致性处理
                //移除超过4个周期的
                nodeHeartbeatService.removeExpired(schedulerCommonProperties.getHeartbeatPeriodSecond() * 4L);
                //移除过期的监控数据
                nodeMonitorService.removeExpired(schedulerCommonProperties.getNodeMonitorExpired());

                lock.getLockInfo().setLockStatus(DistributedLockStatus.SUCCESS);
                distributedLockManager.unLock(lock);
            }
        }catch (Exception e){
            log.warn(e.getMessage(),e);
        }


    }

    @Override
    public void downline(NodeMapStore nodeMapStore) {
        try{
            String host= IpUtil.getIpPort(schedulerCommonProperties.getNodeRealIp(),environment.getProperty(SERVER_PORT));
            nodeHeartbeatService.delete(host,NodeTypeEnum.MANAGER.name());
        }catch (Exception e){
            log.error(e.getMessage(),e);
        }
    }

    /**
     * 注册自己
     */
    private void registrySelf(NodeMapStore nodeMapStore){
        //上报本管理端节点
        try{
            NodeRegistry nodeRegistry=new NodeRegistry();
            nodeRegistry.setNodeType(NodeTypeEnum.MANAGER);
            nodeRegistry.setWorkGroup(SystemConstants.STRING_EMPTY);
            String host= IpUtil.getIpPort(schedulerCommonProperties.getNodeRealIp(),environment.getProperty(SERVER_PORT));
            nodeRegistry.setNodeHost(host);
            nodeRegistry.setCpuUsage(OSUtils.cpuUsage());
            nodeRegistry.setMemoryUsage(OSUtils.memoryUsage());
            nodeRegistry.setLoadAverage(OSUtils.loadAverage());
            nodeRegistry.setLastTime(TimeUtils.nowDate());


            nodeMapStore.addManageNode(nodeRegistry);
            nodeHeartbeatService.UpdateHeartbeat(nodeRegistry);

            NodeMonitor nodeMonitor=new NodeMonitor();
            nodeMonitor.setNodeHost(nodeRegistry.getNodeHost());
            nodeMonitor.setNodeTypeName(nodeRegistry.getNodeType().name());
            nodeMonitor.setWorkGroupName(nodeRegistry.getWorkGroup());
            nodeMonitor.setCpuUsage(nodeRegistry.getCpuUsage());
            nodeMonitor.setMemoryUsage(nodeRegistry.getMemoryUsage());
            nodeMonitor.setLoadAverage(nodeRegistry.getLoadAverage());
            nodeMonitor.setRegTime(nodeRegistry.getLastTime());
            nodeMonitorService.insert(nodeMonitor);

        }catch (Exception e){
            log.error(e.getMessage(),e);
        }

    }

    private List<NodeRegistry> convert(final List<NodeHeartbeat> heartbeatList){
        final List<NodeRegistry> nodeRegistryList= new ArrayList<>();
        if(heartbeatList!=null && !heartbeatList.isEmpty()){
            heartbeatList.forEach(nodeHeartbeat -> {
                nodeRegistryList.add(nodeHeartbeat2NodeRegistryConverter.convert(nodeHeartbeat));
            });
        }

        return nodeRegistryList;
    }
}
